﻿using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Win32;
using System.Xml;
using System.Linq;

namespace AE_Installs
{
    class ZipManager
    {
        /// <summary>
        /// 解一个插件压缩包  释放文件
        /// </summary>
        /// <param name="targetZipPath"></param>
        /// <param name="targetPath"></param>
        /// <param name="password"></param>
        /// <param name="UnzipCallback"></param>
        public static void UnZip(string targetZipPath,string password,string regname,string targetAex,Action<float ,float> UnzipCallback)
        {
            
            string zipname = targetZipPath.Substring(targetZipPath.LastIndexOf("/")+1);
            zipname = zipname.Substring(0, zipname.Length - 4);
            ZipFile zipfile = new ZipFile(File.OpenRead(targetZipPath));
            Console.WriteLine(zipname + "   zipname");

            float now = 0;
            float max = zipfile.Count-2;
            using( ZipInputStream zipInputStream = new ZipInputStream(File.OpenRead(targetZipPath)))
            {
                ZipEntry theEntry;
                while((theEntry = zipInputStream.GetNextEntry()) != null)
                {
                    Console.WriteLine("theEntryName:"+theEntry.Name);
                    string directoryName = Path.GetDirectoryName(theEntry.Name);
                    string fileName = Path.GetFileName(theEntry.Name);
                    string tempname = "";
                    string temppath = "";
                    string targetPath = "";

                    if((!theEntry.Name.Contains(".xml") && !theEntry.Name.Contains(".reg")) )
                    {
                        //释放AEX
                        if (theEntry.Name.Contains(zipname) && theEntry.Name.Contains(targetAex))
                        {
                            targetPath = GetPath();
                            string tt = zipname + "/";
                            tempname = theEntry.Name.Substring(tt.Length - 1, theEntry.Name.Length - tt.Length + 1);
                            temppath = regname + Path.GetDirectoryName(tempname);
                            
                            Console.ForegroundColor = ConsoleColor.Yellow;
                            Console.WriteLine("J:" + targetPath + "  fileName:" + fileName);
                        }

                        //if (fileName != "")
                        {
                            //释放ProgramData
                            if (theEntry.Name.Contains("ProgramData/"))
                            {
                                targetPath = "C:/ProgramData/";
                                string tt = "ProgramData/";
                                tempname = theEntry.Name.Substring(tt.Length - 1, theEntry.Name.Length - tt.Length + 1);
                                temppath = Path.GetDirectoryName(tempname);

                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("Program Data targetPath:" + targetPath + "  fileName:" + fileName);
                            }
                            //释放ProgramFiles
                            else if (theEntry.Name.Contains("Program File/"))
                            {
                                targetPath = "C:/Program Files/";
                                string tt = "Program File/";
                                tempname = theEntry.Name.Substring(tt.Length - 1, theEntry.Name.Length - tt.Length + 1);
                                temppath = Path.GetDirectoryName(tempname);

                                Console.ForegroundColor = ConsoleColor.White;
                            }
                        }

                        directoryName = temppath;

                        if (!string.IsNullOrEmpty(directoryName))
                        {
                            Directory.CreateDirectory(targetPath + directoryName);
                        }
                        if (directoryName != null && !directoryName.EndsWith("/"))
                        {
                        }
                        if (fileName != string.Empty)
                        {
                            zipInputStream.Password = password;
                            using (FileStream streamWriter = File.Create(targetPath + directoryName + "/" + fileName))
                            {
                                int size;
                                byte[] data = new byte[2048];
                                while (true)
                                {
                                    size = zipInputStream.Read(data, 0, data.Length);
                                    if (size > 0)
                                    {
                                        streamWriter.Write(data, 0, size);
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                            }
                        }

                        UnzipCallback?.Invoke(++now, max);
                    }
                }
            }
        }

        static string rootpath = @"C:\Temp\";
        public static void UnZipXml(string targetZipPath, string targetPath, string password, Action<float, float> UnzipCallback)
        {
            string zipname = targetZipPath.Substring(targetZipPath.LastIndexOf("/") + 1);
            zipname = zipname.Substring(0, zipname.Length - 4);
            ZipFile zipfile = new ZipFile(File.OpenRead(targetZipPath));
            Console.WriteLine(zipname + "   zipname");
            float now = 0;
            float max = zipfile.Count; 

            Console.WriteLine("Temp");
            if (Directory.Exists(rootpath))
            {
                Directory.Delete(rootpath,true);
            }
            Directory.CreateDirectory(rootpath);

            using (ZipInputStream zipInputStream = new ZipInputStream(File.OpenRead(targetZipPath)))
            {
                ZipEntry theEntry;
                while ((theEntry = zipInputStream.GetNextEntry()) != null)
                {
                    string directoryName = Path.GetDirectoryName(theEntry.Name);
                    string fileName = Path.GetFileName(theEntry.Name);
                    
                    if (!string.IsNullOrEmpty(directoryName))
                    {
                        if (Directory.Exists(rootpath))
                        {
                            Directory.CreateDirectory(rootpath + directoryName);
                        }
                    }

                    if (fileName != string.Empty && (fileName.Contains(".xml")) || (fileName.Contains(".reg")))
                    {
                        zipInputStream.Password = password;
                        using (FileStream streamWriter = File.Create(rootpath + theEntry.Name))
                        {
                            int size;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = zipInputStream.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                        UnzipCallback?.Invoke(++now, max);
                    }
                    
                }
            }
        }

        public static System.Collections.Generic.Dictionary<string, AEX> AEXDic = new System.Collections.Generic.Dictionary<string, AEX>();

        public static AEX GetAex(string aexname)
        {
            foreach (string s in AEXDic.Keys)
            {
                if (aexname == AEXDic[s].Name || AEXDic[s].ChildrenDirectorys.Where(a => a.Contains(aexname)).Count()>0)
                {
                    return AEXDic[s];
                }
            }

            return null;
        }

        public static void InitUnZipFile()
        {
            string[] files = Directory.GetFiles(rootpath);
            string xmlfilename = "";
            string regfilename = "";
            int count = 0;
            AEX aex = null;
            foreach (string s in files)
            {
                if(s.Contains(".xml"))
                {
                    xmlfilename = s;
                    count++;
                }
                else if(s.Contains(".reg"))
                {
                    regfilename = s;
                    count++;
                }
                if(count==2)
                {
                    break;
                }
            }

            if(xmlfilename != "")
            {
                try
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(xmlfilename);

                    XmlNodeList list = xmlDoc.SelectSingleNode("root").ChildNodes;
                    
                    foreach (XmlNode node in list)
                    {
                        aex = new AEX();
                        string AEXName = node.Attributes["Name"].InnerText;
                        aex.Name = AEXName;
                        XmlNode ChilrenNode = node.SelectSingleNode("Children");

                        XmlNodeList nodelist = ChilrenNode.ChildNodes;
                        string[] Children = new string[nodelist.Count];
                        for (int i = 0; i < Children.Length; ++i)
                        {
                            Children[i] = nodelist[i].Attributes["Name"].InnerText;
                        }
                        aex.SetChildren(Children);

                        XmlNode RelateFilesNode = node.SelectSingleNode("RelateFiles");
                        nodelist = RelateFilesNode.ChildNodes;
                        string[] RelateFiles = new string[nodelist.Count];
                        for (int i = 0; i < RelateFiles.Length; ++i)
                        {
                            RelateFiles[i] = nodelist[i].Attributes["Name"].InnerText + "|" + nodelist[i].Attributes["TargetPath"].InnerText;
                        }
                        aex.SetRelateFiles(RelateFiles);
                        if(!AEXDic.ContainsKey(AEXName))
                        {
                            AEXDic.Add(AEXName, aex);
                        }                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("xml:"+ex.Message.ToString());
                }
            }
            string regName = "";
            if(regfilename != "")
            {
                try
                {
                    using (StreamReader sr = new StreamReader(regfilename))
                    {
                        string readerString = "";

                        readerString = sr.ReadToEnd();
                        if(aex != null)
                        {
                            aex.ResolveRegister(readerString,ref regName);
                            aex.Regname = regName;
                        }
                    }
                }
                catch(Exception ex)
                {
                    Console.WriteLine("reg:"+ex.Message.ToString());
                }
            }

            Console.WriteLine("AEXDic Count："+AEXDic.Count + regName);
        }

        public static void Go(string targetZipPath, string targetPath, string password, string regname,string targetAex, Action<float, float> UnzipCallback)
        {
            UnZip(targetZipPath, targetPath, regname, targetAex, UnzipCallback);
        }
        
        public static void WriteRegister(string registerPath,string json)
        {
            RegistryKey va = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\BorisFX\ContinuumAE\11");
            
            JObject jobj = JsonConvert.DeserializeObject(json)as JObject ;
            
            foreach (JProperty j in jobj.Properties())
            {
                va.SetValue(j.Name ,j.Value);
            }
        }

        public static void AnotherUnZip(string targetZipPath,  string password, Action<float, float> UnzipCallback)
        {
            ZipFile zipfile = new ZipFile(File.OpenRead(targetZipPath));

            float now = 0;
            float max = zipfile.Count;
            string pa = GetPath();
            using (ZipInputStream zipInputStream = new ZipInputStream(File.OpenRead(targetZipPath)))
            {
                ZipEntry theEntry;
                while ((theEntry = zipInputStream.GetNextEntry()) != null)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("theEntryName:" + theEntry.Name);
                    string directoryName = Path.GetDirectoryName(theEntry.Name);
                    string fileName = Path.GetFileName(theEntry.Name);
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("directoryName:" + directoryName + "  fileName:" + fileName);

                    if(theEntry.Name==("J"))
                    {

                    }
                    else if(theEntry.Name==("Program File/BriosFX"))
                    {

                    }
                    else if(theEntry.Name==("Program Data/BriosFX"))
                    {

                    }

                    if (!string.IsNullOrEmpty(directoryName))
                    {
                        Directory.CreateDirectory(pa + directoryName);
                    }
                    if (directoryName != null && !directoryName.EndsWith("/"))
                    {
                    }
                    if (fileName != string.Empty)
                    {
                        zipInputStream.Password = password;
                        using (FileStream streamWriter = File.Create(pa + theEntry.Name))
                        {
                            int size;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = zipInputStream.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }

                    UnzipCallback(++now, max);
                }
            }
        }

        private static void WriteFile(ZipInputStream zipstream,string targetPath,string entryname,string password)
        {
            zipstream.Password = password;
            using (FileStream streamWriter = File.Create(targetPath + entryname))
            {
                int size;
                byte[] data = new byte[2048];
                while (true)
                {
                    size = zipstream.Read(data, 0, data.Length);
                    if (size > 0)
                    {
                        streamWriter.Write(data, 0, size);
                    }
                    else
                    {
                        break;
                    }
                }
                zipstream.Flush();
            }

        }

        private static string GetPath()
        {
            Microsoft.Win32.RegistryKey appPathNode = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths");
            foreach (string subKeyName in appPathNode.GetSubKeyNames())
            {

                Microsoft.Win32.RegistryKey subKey = appPathNode.OpenSubKey(subKeyName);
                object displayName = subKey.GetValue("Path");
                if (subKeyName != null)
                {
                    if (subKeyName.ToString().Contains("AfterFX"))
                    {
                        return displayName.ToString() + "Plug-ins/";
                    }
                }
            }
            return null;

        }

        public static void ZipFile(string targetFile,string zipfilename,string passweord,Action<float,float> ZipCalback)
        {
            using (ZipOutputStream zipOutPutStream = new ZipOutputStream(File.Create(zipfilename)))
            {
                zipOutPutStream.SetLevel(9);
                if(passweord != "" && passweord != null)
                {
                    zipOutPutStream.Password = passweord;
                }

                FileStream fs;
                Crc32 crc = new Crc32();
                ZipEntry zipentry = new ZipEntry("/");
                zipOutPutStream.PutNextEntry(zipentry);

                foreach(string file in Directory.GetDirectories(targetFile))
                {
                    //打开压缩文件
                    fs = File.OpenRead(targetFile);

                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    zipentry = new ZipEntry("/" + Path.GetFileName(targetFile));
                    zipentry.DateTime = DateTime.Now;
                    zipentry.Size = fs.Length;
                    fs.Close();
                    crc.Reset();
                    crc.Update(buffer);
                    zipentry.Crc = crc.Value;
                    zipOutPutStream.PutNextEntry(zipentry);
                    zipOutPutStream.Write(buffer, 0, buffer.Length);
                }
            }
        }

        public static void StreamTest()
        {
            int size = 1024;
            string[] Files = Directory.GetFiles("./AE CS6");
            Console.WriteLine("ReadLength:" + Files[0]);
            //using (FileStream fs = File.OpenRead("./AE CS6"))
            //{
            //    byte[] bs = new byte[size];
            //    int readLength = fs.Read(bs, 0, 1000);
            //    Console.WriteLine("ReadLength:"+readLength);
            //}


            new  System.Threading.Thread( ( FileWrite)).Start("./AE CS6");
            //new System.Threading.Thread((FileRead)).Start("./" + "Jiang.jiang");
        }

        private static void FileWrite(object obj)
        {
            string path = obj as string;
            string filename = Path.GetFileName(path);
            string[] filenames = Directory.GetFiles(path);

            int size = 1024*1024*5;
            using (FileStream fsWrite = new FileStream("./Jiang.bin", FileMode.OpenOrCreate, FileAccess.Write))
            {
                for (int fileindex = 0; fileindex < filenames.Length; ++fileindex)
                {
                    using (FileStream fsRead = new FileStream(filenames[fileindex], FileMode.Open, FileAccess.Read))
                    {
                        byte[] bs = new byte[size];
                        int readLength = 0;

                        Console.WriteLine("ReadLength:" + readLength+" "+filenames[fileindex]);
                        {
                            while (true)
                            {
                                readLength = fsRead.Read(bs, 0, size);
                                if (readLength == 0)
                                {
                                    break;
                                }
                                bs = EncrypBuffer(bs);
                                fsWrite.Write(bs, 0, size);
                            }
                        }
                    }
                }
            }
        }

        private static void FileRead(object o)
        {
            string path = o as string;
            int size = 1024 * 1024 * 5;
            using (FileStream fsRead = new FileStream(path, FileMode.Open, FileAccess.Read))
            {
                byte[] bs = new byte[size];
                int readLength = 0;
                Console.WriteLine("ReadLength:" + readLength);

                using (FileStream fsWrite = new FileStream("./Jiang.zip", FileMode.Create, FileAccess.Write))
                {
                    while (true)
                    {
                        readLength = fsRead.Read(bs, 0, size);
                        if (readLength == 0)
                        {
                            break;
                        }
                        bs = DecryptBuffer(bs);
                        fsWrite.Write(bs, 0, size);
                    }
                }
            }
            //UnZip("./Jiang.zip", "", "", null);
        }

        private static byte[] EncrypBuffer(byte[] bs)
        {
            byte[] srcBs = bs;
            for(int i=0;i<srcBs.Length;++i)
            {
                srcBs[i] = EncryptByte(srcBs[i]);
            }
            return srcBs;
        }

        private static byte[] DecryptBuffer(byte[] bs)
        {
            byte[] srcBs = bs;
            for (int i = 0; i < srcBs.Length; ++i)
            {
                srcBs[i] = DecryptByte(srcBs[i]);
            }
            return srcBs;
        }

        private static byte EncryptByte(byte b)
        {
            int tempb = int.Parse(b.ToString());
            int lastb = tempb + 10;
            int result = lastb > 255 ? (lastb - 255) : lastb;
            return byte.Parse(result.ToString());
        }

        private static byte DecryptByte(byte b)
        {
            int tempb = int.Parse(b.ToString());
            int lastb = tempb - 10;
            int result = lastb < 0 ? ((lastb + 255)-10) : lastb;
            return byte.Parse(result.ToString());
        }
    }
}
